/*
 * $Id: GeodataService.java 43 2013-04-19 15:16:56Z gabakyan $
 * $Author: gabakyan $
 * $Revision: 43 $
 * $Date: 2013-04-19 15:16:56 +0000 (Fri, 19 Apr 2013) $
 *
 * Copyright (c) 2013 Supply Chain Intelligence (SCI), Inc.
 * http://www.scintelligence.com/, Email: info@scintelligence.com
 * All rights reserved.
 *
 * This file is part of Logistics Map.
 *
 * Logistics Map is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * Logistics Map is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Logistics Map.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.sci.logisticsmap.service;

import au.com.bytecode.opencsv.CSVReader;
import com.google.code.geocoder.model.DirectionsStatus;
import com.sci.logisticsmap.data.model.*;
import com.sci.logisticsmap.data.model.location.DCModel;
import com.sci.logisticsmap.data.model.location.DeliveryModel;
import com.sci.logisticsmap.data.model.location.PickupModel;
import com.sci.logisticsmap.data.model.location.ReturnModel;
import com.sci.logisticsmap.data.repository.GeodataRepository;
import com.sci.logisticsmap.data.repository.LocationRepository;
import com.sci.logisticsmap.data.repository.UserRepository;
import com.sci.logisticsmap.data.support.comparator.LocationModelPositionComparator;
import com.sci.logisticsmap.data.support.sorting.GeodataModelSorting;
import com.sci.logisticsmap.service.model.*;
import com.sci.logisticsmap.service.model.location.DCDTModel;
import com.sci.logisticsmap.service.model.UpdateGeodataLocationRequest;
import com.sci.logisticsmap.service.model.UpdateGeodataLocationsRequest;
import com.sci.logisticsmap.service.model.GeodataResponse;
import com.sci.logisticsmap.service.support.GeodataUtilities;
import com.sci.logisticsmap.service.support.LocationImportUtilities;
import com.sci.logisticsmap.service.support.RuntimeConfigurationInstance;
import com.sci.logisticsmap.service.support.ServiceException;
import com.sci.logisticsmap.service.support.builder.response.*;
import com.google.code.geocoder.DirectionsGeocoder;
import com.sci.logisticsmap.support.*;
import com.sci.logisticsmap.support.model.ResponseWrapper;
import com.sci.logisticsmap.support.model.*;
import com.sci.logisticsmap.support.model.geocoding.LatLng;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.util.*;

/**
 * The Service for working with Geodata model.
 *
 * @author gabakyan@gmail.com (Last modified by $Author: gabakyan $)
 * @version $Revision: 43 $
 */
@Service
@Transactional
public class GeodataService extends BaseService {
    /**
     * The Geodata repository.
     */
    @Autowired
    private GeodataRepository geodataRepository;
//    /**
//     * The Geodata Locations repository.
//     */
//    @Autowired
//    private GeodataLocationsRepository geodataLocationsRepository;
    /**
     * The Location repository.
     */
    @Autowired
    private LocationRepository/*<? extends LocationDTModel>*/ locationRepository;

//    /**
//     * The Location import template repository.
//     */
//    @Autowired
//    private LocationImportTemplateRepository locationImportTemplateRepository;
    /**
     * The user repository.
     */
    @Autowired
    private UserRepository userRepository;

    /**
     * The max imported Locations count.
     */
    private @Value("${geodataService.maxImportedLocationCount}") int maxImportedLocationCount;

//    /**
//     * The geocoder.
//     */
//    @Autowired
//    private Geocoder geocoder;
    /**
     * The directions geocoder.
     */
    @Autowired
    private DirectionsGeocoder directionsGeocoder;

    /**
     * The Location model position comparator.
     */
    @Autowired
    private LocationModelPositionComparator locationModelPositionComparator;

//    /**
//     * The geocoding default pausing interval.
//     */
//    private static final long geocodingDefaultPausingInterval = 100;
//    /**
//     * The geocoding over query limit pausing interval.
//     */
//    private static final long geocodingOverQueryLimitPausingInterval = 2000;

    /**
     * The supported file format string.
     */
    private static final String SUPPORTED_FILE_FORMAT_STRING = "Type, Label, Address, City, ST, Zip";//, Lat, Lon";

    /**
     * Deletes Locations by the session id.
     *
     * @param sessionId The session id.
     */
    public void deleteBySessionId(String sessionId) {
        try {
            List<GeodataModel> geodataList = geodataRepository.findBySessionId(sessionId);
            if (geodataList != null) {
                geodataRepository.delete(geodataList);
                for (GeodataModel geodata : geodataList) {
                    //geodataRepository.delete(geodata);
                    //GeodataLocations geodataLocations = geodataLocationsRepository.findByGeodataId(geodata.getId());
                    //geodataLocationsRepository.delete(geodataLocations);
                    List<? extends LocationModel> locations = locationRepository.findByGeodataId(geodata.getId());
                    locationRepository.delete(locations);
                }
            }
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

//    /**
//     * Deletes all Locations.
//     */
    /*public void deleteAll() {
        try {
            geodataRepository.deleteAll();
            //geodataLocationsRepository.deleteAll();
            locationRepository.deleteAll();
            *//*List<Geodata> geodataList = geodataRepository.findAll();
            if (geodataList != null) {
                geodataRepository.delete(geodataList);
                for (Geodata geodata : geodataList) {
                    //geodataRepository.delete(geodata);
                    //GeodataLocations geodataLocations = geodataLocationsRepository.findByGeodataId(geodata.getId());
                    //geodataLocationsRepository.delete(geodataLocations);
                    List<? extends Location> locations = locationRepository.findByGeodataId(geodata.getId());
                    locationRepository.delete(locations);
                }
            }*//*
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }*/

    /**
     * Retrieves the Geodata model wrapped response.
     *
     * @param id The Geodata id.
     //* @param username The username.
     * @param sessionId The session id.
     *
     * @return The Geodata model wrapped response.
     */
    public ResponseWrapper<GeodataDTModel> getDTModelResponse(String id, /*String username*/String sessionId) {
        try {
            GeodataDTModelResponseBuilder builder = new GeodataDTModelResponseBuilder(conversionUtilities);

            GeodataModel geodata = geodataRepository.findOne(id);
            if (geodata != null) {
                checkGeodataSessionId(geodata, sessionId);

                //GeodataLocations geodataLocations = geodataLocationsRepository.findByGeodataId(id);
                List<? extends LocationModel> locations = locationRepository.findByGeodataId(geodata.getId());
                builder.setGeodata(geodata);
                //builder.setGeodataLocations(geodataLocations);
                builder.setLocations(locations);
            }
            return builder.buildWrapper();
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    /**
     * Retrieves the Geodata wrapped response.
     *
     * @param id The Geodata id.
     //* @param username The username.
     * @param sessionId The session id.
     *
     * @return The Geodata wrapped response.
     */
    public ResponseWrapper<GeodataResponse> getGeodataResponse(String id, /*String username*/String sessionId) {
        try {
            GeodataModel geodata = geodataRepository.findOne(id);
            if (geodata != null) {
                //GeodataLocations geodataLocations = geodataLocationsRepository.findByGeodataId(id);
                //return buildGeodataResponse(geodata, geodataLocations, /*username*/sessionId);
                List<? extends LocationModel> locations = locationRepository.findByGeodataId(geodata.getId());
                return buildGeodataResponse(geodata, locations, /*username*/sessionId);
            }
            return null;
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    /**
     * Retrieves the Geodata light models wrapped response by the username.
     *
     * @param username The username.
     *
     * @return The Geodata light models wrapped response.
     */
    public ResponseWrapper<ArrayList<GeodataLightDTModel>> getLightDTModelsResponseByUsername(String username) {
        try {
            GeodataLightDTModelsResponseBuilder builder = new GeodataLightDTModelsResponseBuilder(conversionUtilities);
            //User user = userRepository.findOne(username);
            //List<GeodataModel> geodataList = geodataRepository.findByUser(user);
            List<GeodataModel> geodataList = geodataRepository.findByUsername(username, GeodataModelSorting.BY_CREATION_DATE_DESC);
            if (geodataList != null) {
                builder.setGeodataList(geodataList);
            }
            return builder.buildWrapper();
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    /**
     * Retrieves the Geodata light models wrapped response by the session id.
     *
     * @param sessionId The session id.
     *
     * @return The Geodata light models wrapped response.
     */
    public ResponseWrapper<ArrayList<GeodataLightDTModel>> getLightDTModelsResponseBySessionId(String sessionId) {
        try {
            GeodataLightDTModelsResponseBuilder builder = new GeodataLightDTModelsResponseBuilder(conversionUtilities);
            List<GeodataModel> geodataList = geodataRepository.findBySessionId(sessionId, GeodataModelSorting.BY_CREATION_DATE_DESC);
            if (geodataList != null) {
                builder.setGeodataList(geodataList);
            }
            return builder.buildWrapper();
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

//    /**
//     * Retrieves the Geodata Location light models wrapped response by the status.
//     *
//     //* @param username The username.
//     * @param sessionId The session id.
//     * @param geodataId The Geodata id.
//     * @param locationStatus The Location status.
//     *
//     * @return The Geodata Location light models wrapped response.
//     */
//    public ResponseWrapper<ArrayList<LocationLightDTModel>> getGeodataLocationLightDTModelsResponseByStatus(/*String username*/String sessionId, String geodataId, String locationStatus) {return null;
////        try {
////            GeodataLightDTModelListResponseBuilder builder = new GeodataLightDTModelListResponseBuilder(conversionUtilities);
////            List<GeodataModel> geodataList = geodataLocationsRepository.findByLocationStatus(sessionId, GeodataModelSorting.BY_CREATION_DATE_DESC);
////            if (geodataList != null) {
////                builder.setGeodataList(geodataList);
////            }
////            return builder.buildWrapper();
////        } catch (Exception e) {
////            throw new ServiceException(e);
////        }
//    }
//    public ResponseWrapper<ArrayList<LocationLightDTModel>> getNotGeocodedGeodataLocationLightDTModelsResponse(/*String username*/String sessionId, String geodataId) {
//        return getGeodataLocationLightDTModelsByStatus(/*username*/sessionId, geodataId, LocationStatus.NG);
//    }

    /**
     * Retrieves the Geodata Location model wrapped response.
     *
     * @param id The Location id.
     * @param geodataId The Geodata id.
     //* @param username The username.
     * @param sessionId The session id.
     *
     * @return The Geodata Location model wrapped response.
     */
    public ResponseWrapper<LocationDTModel> getGeodataLocationDTModelResponse(String id, String geodataId, /*String username*/String sessionId) {
        try {
            GeodataModel geodata = geodataRepository.findOne(geodataId);
            if (geodata != null) {
                checkGeodataSessionId(geodata, sessionId);

                /*GeodataLocationsModel geodataLocations = geodataLocationsRepository.findByGeodataId(geodataId);
                if (*//*geodata*//*geodataLocations != null) {
                    LocationDTModelResponseBuilder builder = new LocationDTModelResponseBuilder(conversionUtilities);
                    for (LocationModel location : *//*geodata*//*geodataLocations.getLocations()) {
                        if (location.getId().equals(id)) {
                            builder.setLocation(location);
                            return builder.buildWrapper();
                        }
                    }
                }*/
                LocationModel location = locationRepository.findOne(id);
                if (location != null && location.getGeodataId() != null && location.getGeodataId().equals(geodataId)) {
                    LocationDTModelResponseBuilder builder = new LocationDTModelResponseBuilder(conversionUtilities);
                    builder.setLocation(location);
                    return builder.buildWrapper();
                }
            }
            return null;
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    /**
     * Updates the Geodata Location.
     *
     * @param requestObject The request object.
     //* @param username The username.
     * @param sessionId The session id.
     *
     * @return The boolean wrapped response.
     */
    public ResponseWrapper<Boolean> updateGeodataLocation(
            UpdateGeodataLocationRequest requestObject
            /*String id, Double lat, Double lng, String geodataId*/, /*String username*/String sessionId, String username) {
        try {
            LocationDTModel newLocation = requestObject.getLocation();
            String geodataId = requestObject.getGeodataId();
            GeodataModel geodata = geodataRepository.findOne(geodataId);
            //logger.debug("updateGeodataLocation: geodata = {}/{}", geodata, geodataId);
            if (geodata != null) {
                checkGeodataSessionId(geodata, sessionId);

                //GeodataLocations geodataLocations = geodataLocationsRepository.findByGeodataId(geodataId);

                UserModel user = userRepository.findOne(username);
                UserModel.Configuration userConfiguration = user.getConfiguration();

                int geocodedLocationCount = userConfiguration.getGeocodedLocationCount();
                int geocodedDirectionsCount = userConfiguration.getGeocodedDirectionsCount();

                GeodataStatistics statistics = geodata.getStatistics();
                int statisticsGeocodedLocationCount = statistics.getGeocodedLocationCount();
                int statisticsNotGeocodedLocationCount = statistics.getNotGeocodedLocationCount();
                int statisticsGeocodingFailedLocationCount = statistics.getGeocodingFailedLocationCount();

                boolean found = false;
                //List<LocationModel> locations = geodata.getLocations();
                //logger.debug("\tlocations.size() = {}", locations.size());
                //for (LocationModel location : geodataLocations.getLocations()) {
                //    if (location.getId().equals(newLocation.getId())) {
                    LocationModel location = locationRepository.findOne(newLocation.getId());
                    if (location != null && location.getGeodataId() != null && location.getGeodataId().equals(geodataId)) {
                        //logger.debug("\tlocation.getClass() = {}", location.getClass());
                        if (newLocation.getStatus() != null) {
                            if (newLocation.getStatus().equals(LocationStatus.G)) {
                                geocodedLocationCount++;
                                if (location.getStatus() != null) {
                                    if (!location.getStatus().equals(LocationStatus.G)) {
                                        statisticsGeocodedLocationCount++;

                                        if (location.getStatus().equals(LocationStatus.NG)) {
                                            statisticsNotGeocodedLocationCount--;
                                        } else if (location.getStatus().equals(LocationStatus.GF)) {
                                            statisticsGeocodingFailedLocationCount--;
                                        }
                                    }
                                }

                                if (!(newLocation instanceof DCDTModel)) {
                                    geocodedDirectionsCount++;
                                }
                            } else if (newLocation.getStatus().equals(LocationStatus.GF)) {
                                if (location.getStatus() != null) {
                                    if (!location.getStatus().equals(LocationStatus.GF)) {
                                        if (location.getStatus().equals(LocationStatus.G)) {
                                            statisticsGeocodedLocationCount--;
                                        } else if (location.getStatus().equals(LocationStatus.NG)) {
                                            statisticsNotGeocodedLocationCount--;
                                        }
                                        statisticsGeocodingFailedLocationCount++;
                                    }
                                }
                            }
                        }

                        translationUtilities.translate(newLocation, location);

                        found = true;
                        //break;
                    }
                //}

                if (found) {
                    statistics.setGeocodedLocationCount(statisticsGeocodedLocationCount);
                    statistics.setNotGeocodedLocationCount(statisticsNotGeocodedLocationCount);
                    statistics.setGeocodingFailedLocationCount(statisticsGeocodingFailedLocationCount);

                    geodataRepository.save(geodata);
                    //geodataLocationsRepository.save(geodataLocations);
                    locationRepository.save(location);
                    logger.debug("updateGeodataLocation: Geodata saved successfully");

                    userConfiguration.setGeocodedLocationCount(geocodedLocationCount);
                    userConfiguration.setGeocodedDirectionsCount(geocodedDirectionsCount);
                    userRepository.save(user);

                    BooleanResponseBuilder builder = new BooleanResponseBuilder();
                    builder.setBoolean(true);
                    return builder.buildWrapper();
                }
            }

//            //Geodata geodata = geodataRepository.findOne(geodataId);
//            GeodataLocations geodataLocations = geodataLocationsRepository.findByGeodataId(geodataId);
//            if (/*geodata*/geodataLocations != null) {
//                User user = userRepository.findOne(username);
//                User.Configuration userConfiguration = user.getConfiguration();
//
//                int geocodedLocationCount = userConfiguration.getGeocodedLocationCount();
//                int geocodedDirectionsCount = userConfiguration.getGeocodedDirectionsCount();
//
//                boolean found = false;
//                boolean foundDcLocation = false;
//                Location dcLocation = null;
//                List<Location> locations = new ArrayList<Location>();
//                for (Location location : /*geodata*/geodataLocations.getLocations()) {
//                    if (location.getId().equals(id)) {
//                        LatLng latLng = location.getLatLng();
//                        latLng.setLat(new BigDecimal(lat));
//                        latLng.setLng(new BigDecimal(lng));
//
//                        if (location instanceof DC) {
//                            locations.addAll(/*geodata*/geodataLocations.getLocations());
//                            dcLocation = location;
//                            foundDcLocation = true;
//                        } else {
//                            locations.add(location);
//                            dcLocation = location.getDc();
//                        }
//
//                        found = true;
//                        break;
//                    }
//                }
//
//                if (found) {
//                    boolean dcGeocoded = false;
//                    boolean dcGeocodingCounted = false;
//                    LatLng dcLatLng = dcLocation.getLatLng();
//                    for (Location location : locations) {
//                        if (location.getStatus().equals(LocationStatus.GF) || location instanceof DC) {
//                            continue;
//                        }
//
//                        location.setRoutes(null);
//
//                        LatLng latLng = location.getLatLng();
//
//                        logger.debug("Geocoding directions: from {}, to {}...", dcLatLng, latLng);
//
//                        int attempts = 0;
//                        boolean geocodingFailed = true;
//                        while (attempts < 3) {
//                            DirectionsRequest directionsRequest = new DirectionsRequestBuilder().setOrigin(dcLatLng).setDestination(latLng).getDirectionsRequest();
//                            DirectionsResponse directionsResponse = directionsGeocoder.directions(directionsRequest);
//                            logger.debug("Geocoding response: {}", directionsResponse);
//
//                            if (directionsResponse != null && directionsResponse.getStatus() != null) {
//                                if (directionsResponse.getStatus().equals(DirectionsStatus.OK)) {
//                                    List<com.google.code.geocoder.model.DirectionsRoute> routes = directionsResponse.getRoutes();
//
//                                    //com.google.code.geocoder.model.DirectionsRoute route = routes.get(0);
//                                    //List<com.google.code.geocoder.model.DirectionsLeg> legs = route.getLegs();
//
//                                    if (foundDcLocation && !dcGeocoded) {
//                                        //com.google.code.geocoder.model.LatLng dcLatLng = legs.get(0).getStartLocation();
//                                        //logger.debug("\tdcLat = {}, dcLng = {}", dcLatLng.getLat(), dcLatLng.getLng());
//
//                                        //dcLocation.setLatLng(geocoderLatLngToLatLng.convert(dcLatLng));
//                                        dcLocation.setStatus(LocationStatus.G);
//
//                                        if (!dcGeocodingCounted) {
//                                            geocodedLocationCount++;
//                                            dcGeocodingCounted = true;
//                                        }
//
//                                        dcGeocoded = true;
//                                    }
//
//                                    //com.google.code.geocoder.model.LatLng latLng = legs.get(legs.size() - 1).getEndLocation();
//                                    //logger.debug("\tlat = {}, lng = {}", latLng.getLat(), latLng.getLng());
//
//                                    //location.setLatLng(geocoderLatLngToLatLng.convert(latLng));
//                                    location.setRoutes(geocoderDirectionsRouteToDirectionsRoute.convert(routes));
//                                    location.setStatus(LocationStatus.G);
//
//                                    // All web services. Rate limit of 10 requests per second, per web service.
//                                    pauseGeocoding(geocodingDefaultPausingInterval, directionsResponse.getStatus());
//
//                                    geocodingFailed = false;
//                                    break;
//                                } else if (directionsResponse.getStatus().equals(DirectionsStatus.OVER_QUERY_LIMIT)) {
//                                    // Upon receiving a response with status code OVER_QUERY_LIMIT, your application should determine which usage limit has been exceeded.
//                                    // This can be done by pausing for 2 seconds and resending the same request. If status code is still OVER_QUERY_LIMIT, your application is sending too many requests per day.
//                                    // Otherwise, your application is sending too many requests per second.
//                                    pauseGeocoding(geocodingOverQueryLimitPausingInterval, directionsResponse.getStatus());
//                                } else {
//                                    // All web services. Rate limit of 10 requests per second, per web service.
//                                    pauseGeocoding(geocodingDefaultPausingInterval, directionsResponse.getStatus());
//
//                                    break;
//                                }
//                            }
//
//                            attempts++;
//                        }
//
//                        if (geocodingFailed) {
//                            logger.debug("geocoding failed");
//
//                            if (!dcGeocoded) {
//                                dcLocation.setStatus(LocationStatus.GF);
//
//                                if (!dcGeocodingCounted) {
//                                    geocodedLocationCount++;
//                                    dcGeocodingCounted = true;
//                                }
//                            }
//
//                            location.setStatus(LocationStatus.GF);
//                        }
//
//                        geocodedLocationCount++;
//                        geocodedDirectionsCount++;
//
//                        LocationDTModelResponseBuilder builder = new LocationDTModelResponseBuilder(conversionUtilities);
//                        //builder.setLocation(location);
//                        //return builder.buildWrapper();
//                    }
//
//                    geodataRepository.save(geodata);
//                    logger.debug("Geodata saved successfully");
//
//                    userConfiguration.setGeocodedLocationCount(geocodedLocationCount);
//                    userConfiguration.setGeocodedDirectionsCount(geocodedDirectionsCount);
//                    userRepository.save(user);
//
//                    return buildGeodataResponse(geodata, /*username*/sessionId);
//                }
//            }
            return null;
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    /**
     * Updates the Geodata Locations.
     *
     * @param requestObject The request object.
     //* @param username The username.
     * @param sessionId The session id.
     *
     * @return The boolean wrapped response.
     */
    public ResponseWrapper<Boolean> updateGeodataLocations(
            UpdateGeodataLocationsRequest requestObject, /*String username*/String sessionId, String username) {
        try {
            List<? extends LocationDTModel> newLocations = requestObject.getLocations();
            String geodataId = requestObject.getGeodataId();
            GeodataModel geodata = geodataRepository.findOne(geodataId);
            //logger.debug("updateGeodataLocations: geodata = {}/{}", geodata, geodataId);
            if (geodata != null) {
                checkGeodataSessionId(geodata, sessionId);

                //GeodataLocationsModel geodataLocations = geodataLocationsRepository.findByGeodataId(geodataId);

                UserModel user = userRepository.findOne(username);
                UserModel.Configuration userConfiguration = user.getConfiguration();

                int geocodedLocationCount = userConfiguration.getGeocodedLocationCount();
                int geocodedDirectionsCount = userConfiguration.getGeocodedDirectionsCount();

                GeodataStatistics statistics = geodata.getStatistics();
                int statisticsGeocodedLocationCount = statistics.getGeocodedLocationCount();
                int statisticsNotGeocodedLocationCount = statistics.getNotGeocodedLocationCount();
                int statisticsGeocodingFailedLocationCount = statistics.getGeocodingFailedLocationCount();

                //List<LocationModel> locations = geodata.getLocations();
                List<LocationModel> updatedLocations = new ArrayList<LocationModel>();
                //logger.debug("\tlocations.size() = {}", locations.size());
                /*Map<String, LocationModel> locationMap = new HashMap<String, LocationModel>();
                for (LocationModel location : geodataLocations.getLocations()) {
                    //if (location.getStatus() != null && location.getStatus().equals(LocationStatus.NG)) {
                        locationMap.put(location.getId(), location);
                    //}
                }*/

                for (LocationDTModel newLocation : newLocations) {
                    //logger.debug("\tnewLocation.getId() = {}", newLocation.getId());
                    //LocationModel location = locationMap.get(newLocation.getId());
                    //if (location != null) {
                    LocationModel location = locationRepository.findOne(newLocation.getId());
                    if (location != null && location.getGeodataId() != null && location.getGeodataId().equals(geodataId)) {
                        //logger.debug("\tlocation.getClass() = {}", location.getClass());
                        if (newLocation.getStatus() != null) {
                            if (newLocation.getStatus().equals(LocationStatus.G)) {
                                geocodedLocationCount++;
                                if (location.getStatus() != null) {
                                    if (!location.getStatus().equals(LocationStatus.G)) {
                                        statisticsGeocodedLocationCount++;

                                        if (location.getStatus().equals(LocationStatus.NG)) {
                                            statisticsNotGeocodedLocationCount--;
                                        } else if (location.getStatus().equals(LocationStatus.GF)) {
                                            statisticsGeocodingFailedLocationCount--;
                                        }
                                    }
                                }

                                if (!(newLocation instanceof DCDTModel)) {
                                    geocodedDirectionsCount++;
                                }
                            } else if (newLocation.getStatus().equals(LocationStatus.GF)) {
                                if (location.getStatus() != null) {
                                    if (!location.getStatus().equals(LocationStatus.GF)) {
                                        if (location.getStatus().equals(LocationStatus.G)) {
                                            statisticsGeocodedLocationCount--;
                                        } else if (location.getStatus().equals(LocationStatus.NG)) {
                                            statisticsNotGeocodedLocationCount--;
                                        }
                                        statisticsGeocodingFailedLocationCount++;
                                    }
                                }
                            }
                        }

                        translationUtilities.translate(newLocation, location);

                        // fixes location's LatLng precision.
                        if (location.getLatLng() != null) {
                            GeodataUtilities.fixLatLngPrecision(location.getLatLng());
                        }

                        updatedLocations.add(location);
                    }
                }

                statistics.setGeocodedLocationCount(statisticsGeocodedLocationCount);
                statistics.setNotGeocodedLocationCount(statisticsNotGeocodedLocationCount);
                statistics.setGeocodingFailedLocationCount(statisticsGeocodingFailedLocationCount);

                geodataRepository.save(geodata);
                //geodataLocationsRepository.save(geodataLocations);
                locationRepository.save(updatedLocations);
                logger.debug("updateGeodataLocations: Geodata saved successfully");

                userConfiguration.setGeocodedLocationCount(geocodedLocationCount);
                userConfiguration.setGeocodedDirectionsCount(geocodedDirectionsCount);
                userRepository.save(user);

                BooleanResponseBuilder builder = new BooleanResponseBuilder();
                builder.setBoolean(true);
                return builder.buildWrapper();
            }
            return null;
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    /**
     * Deletes the Geodata Location.
     *
     * 2013-04-19, Don't Remove a Stop Forever
     *
     * @param id The Location id.
     * @param geodataId The Geodata id.
     //* @param username The username.
     * @param sessionId The session id.
     *
     * @return The wrapped boolean <true> if was deleted, otherwise <code>false</code>.
     */
    public ResponseWrapper<Boolean> deleteGeodataLocation(String id, String geodataId, /*String username*/String sessionId) {
        try {
            GeodataModel geodata = geodataRepository.findOne(geodataId);
            if (geodata != null) {
                checkGeodataSessionId(geodata, sessionId);

                //GeodataLocationsModel geodataLocations = geodataLocationsRepository.findByGeodataId(geodataId);
                //List<? extends LocationModel> locations = locationRepository.findByGeodataId(geodata.getId());

                // 2013-04-19, Don't Remove a Stop Forever
                // No need to change the Geodata statistics
                //GeodataStatistics statistics = geodata.getStatistics();
                //int statisticsGeocodedLocationCount = statistics.getGeocodedLocationCount();
                //int statisticsNotGeocodedLocationCount = statistics.getNotGeocodedLocationCount();
                //int statisticsGeocodingFailedLocationCount = statistics.getGeocodingFailedLocationCount();

                boolean found = false;
                //for (LocationModel location : /*geodata*/geodataLocations.getLocations()) {
                //    if (location.getId().equals(id)) {
                //        /*geodata*/geodataLocations.getLocations().remove(location);
                    LocationModel location = locationRepository.findOne(id);
                    if (location != null && location.getGeodataId() != null && location.getGeodataId().equals(geodataId)) {
                        // 2013-04-19, Don't Remove a Stop Forever
                        //locationRepository.delete(location);

                        // 2013-04-19, Don't Remove a Stop Forever (replacement)
                        location.setDeleted(true);
                        locationRepository.save(location);

                        logger.debug("deleteGeodataLocation: location deleted (deleted flag was changed to true)");

                        // 2013-04-19, Don't Remove a Stop Forever
                        // No need to change the Geodata statistics
                        /*if (location.getStatus() != null) {
                            if (location.getStatus().equals(LocationStatus.G)) {
                                statisticsGeocodedLocationCount--;
                            } else if (location.getStatus().equals(LocationStatus.NG)) {
                                statisticsNotGeocodedLocationCount--;
                            } else if (location.getStatus().equals(LocationStatus.GF)) {
                                statisticsGeocodingFailedLocationCount--;
                            }
                        }*/

                        found = true;
                        //break;
                    }
                //}

                if (found) {
                    // 2013-04-19, Don't Remove a Stop Forever
                    // No need to change the Geodata statistics
                    //statistics.setGeocodedLocationCount(statisticsGeocodedLocationCount);
                    //statistics.setNotGeocodedLocationCount(statisticsNotGeocodedLocationCount);
                    //statistics.setGeocodingFailedLocationCount(statisticsGeocodingFailedLocationCount);
                    //
                    //geodataRepository.save(geodata);
                    ////geodataLocationsRepository.save(geodataLocations);
                    //logger.debug("deleteGeodataLocation: Geodata saved successfully");

                    BooleanResponseBuilder builder = new BooleanResponseBuilder();
                    builder.setBoolean(true);
                    return builder.buildWrapper();
                }
            }
            return null;
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    /**
     * Restores the Geodata Location.
     *
     * 2013-04-19, Don't Remove a Stop Forever
     * Allow user to 'enable' it (removed Stop) so then it reappears.
     *
     * @param id The Location id.
     * @param geodataId The Geodata id.
     //* @param username The username.
     * @param sessionId The session id.
     *
     * @return The wrapped boolean <true> if was restored, otherwise <code>false</code>.
     */
    public ResponseWrapper<Boolean> restoreGeodataLocation(String id, String geodataId, /*String username*/String sessionId) {
        try {
            GeodataModel geodata = geodataRepository.findOne(geodataId);
            if (geodata != null) {
                checkGeodataSessionId(geodata, sessionId);

                //GeodataLocationsModel geodataLocations = geodataLocationsRepository.findByGeodataId(geodataId);
                //List<? extends LocationModel> locations = locationRepository.findByGeodataId(geodata.getId());

                boolean found = false;
                //for (LocationModel location : /*geodata*/geodataLocations.getLocations()) {
                //    if (location.getId().equals(id)) {
                //        /*geodata*/geodataLocations.getLocations().remove(location);
                    LocationModel location = locationRepository.findOne(id);
                    if (location != null && location.getGeodataId() != null && location.getGeodataId().equals(geodataId)) {
                        location.setDeleted(false);
                        locationRepository.save(location);

                        logger.debug("restoreGeodataLocation: location restored (deleted flag was changed to false)");

                        found = true;
                        //break;
                    }
                //}

                if (found) {
                    BooleanResponseBuilder builder = new BooleanResponseBuilder();
                    builder.setBoolean(true);
                    return builder.buildWrapper();
                }
            }
            return null;
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    /**
     * Imports Locations.
     *
     * @param sessionUser The session user.
     * @param sessionId The session id.
     //* @param locationImportTemplateId The Location import template id.
     * @param mimeType The MIME Type.
     * @param inputStream The input stream.
     //* @param bytes The bytes.
     *
     * @return The Geodata wrapped response.
     */
    public ResponseWrapper<GeodataResponse> importLocations(SessionUserDTModel sessionUser, String sessionId/*, String locationImportTemplateId*/, String mimeType, InputStream inputStream/*byte[] bytes*/) {
        try {
            UserModel user = userRepository.findOne(sessionUser.getUsername());
            UserModel.Configuration userConfiguration = user.getConfiguration();

            int importedLocationCount = userConfiguration.getImportedLocationCount();
            //int geocodedLocationCount = userConfiguration.getGeocodedLocationCount();
            //int geocodedDirectionsCount = userConfiguration.getGeocodedDirectionsCount();
            int initialImportedLocationCount = importedLocationCount;

            checkImportedLocationCount(importedLocationCount, initialImportedLocationCount);

            //LocationImportTemplateModel locationImportTemplate = locationImportTemplateRepository.findOne(locationImportTemplateId);
            LocationImportTemplateDTModel locationImportTemplate = RuntimeConfigurationInstance.INSTANCE.getPrimaryLocationImportTemplate();
            if (locationImportTemplate != null) {
                //List<LocationImportTemplate.Field> fields = locationImportTemplate.getFields();
                List<LocationImportTemplateDTModel.Field> fields = locationImportTemplate.getFields();

                List<String[]> lineStringsList = new ArrayList<String[]>();
                /*if (LocationImportUtilities.isPlainTextMimeType(mimeType)) {
                    //String inputString = new String(bytes, Constants.DEFAULT_ENCODING);
                    //BufferedReader reader = new BufferedReader(new StringReader(inputString));
                    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                    try {
                        String line;
                        int lineCounter = 0;
                        while ((line = reader.readLine()) != null && ++importedLocationCount <= maxImportedLocationCount) {
                            logger.debug("importLocations: parsing file line: {}, {}", line, String.valueOf(importedLocationCount));
                            lineCounter++;
                            String[] lineStrings = line.split(",");
                            if (lineStrings.length >= fields.size()) {
                                lineStringsList.add(lineStrings);
                            } else {
                                throw new IllegalStateException(Utilities.buildString("Unsupported file format in line ", lineCounter, " \"", line, "\". Supported file format is \"", SUPPORTED_FILE_FORMAT_STRING, "\"."));
                            }
                        }
                    } finally {
                        reader.close();
                    }
                } else if (LocationImportUtilities.isCSVMimeType(mimeType)) {*/
                if (LocationImportUtilities.isSupportedLocationImportMimeType(mimeType)) {
                    CSVReader reader = new CSVReader(new InputStreamReader(inputStream));
                    try {
                        String[] lineStrings;
                        int lineCounter = 0;
                        while ((lineStrings = reader.readNext()) != null && ++importedLocationCount <= maxImportedLocationCount) {
                            logger.debug("importLocations: parsing file line: {}, {}", lineStrings, String.valueOf(importedLocationCount));
                            lineCounter++;
                            if (lineStrings.length >= fields.size()) {
                                lineStringsList.add(lineStrings);
                            } else {
                                throw new IllegalStateException(Utilities.buildString("Unsupported file format in line ", lineCounter, " \"", Utilities.buildCommaSeparatedString(lineStrings), "\". Supported file format is \"", SUPPORTED_FILE_FORMAT_STRING, "\"."));
                            }
                        }
                    } finally {
                        reader.close();
                    }
                } else {
                    throw new IllegalStateException(Utilities.buildString("Unsupported MIME Type \"", mimeType, "\"."));
                }

                checkImportedLocationCount(importedLocationCount, initialImportedLocationCount);

                List<LocationModel> locations = new ArrayList<LocationModel>();
                DCModel dcLocation = null;

                String type;
                String dc;
                String label;
                String address;
                String city;
                String state;
                String zipCode;
                String country;
                //BigDecimal lat;
                //BigDecimal lng;

                //int i = 0;
                for (String[] lineStrings : lineStringsList) {
                    type = null;
                    dc = null;
                    label = null;
                    address = null;
                    city = null;
                    state = null;
                    zipCode = null;
                    country = null;
                    //lat = null;
                    //lng = null;

                    int j = 0;
                    //for (LocationImportTemplateModel.Field field : fields) {
                    for (LocationImportTemplateDTModel.Field field : fields) {
                        String fieldType = field.getType();
                        String lineString = lineStrings[j];

                        if (StringUtils.isNotBlank(lineString)) {
                            if (fieldType.equals(LocationImportTemplateFieldType.TYPE)) {
                                type = lineString;
                            } else if (fieldType.equals(LocationImportTemplateFieldType.DC)) {
                                dc = lineString;
                            } else if (fieldType.equals(LocationImportTemplateFieldType.LABEL)) {
                                label = lineString;
                            } else if (fieldType.equals(LocationImportTemplateFieldType.ADDRESS)) {
                                address = lineString;
                            } else if (fieldType.equals(LocationImportTemplateFieldType.CITY)) {
                                city = lineString;
                            } else if (fieldType.equals(LocationImportTemplateFieldType.STATE)) {
                                state = lineString;
                            } else if (fieldType.equals(LocationImportTemplateFieldType.ZIP_CODE)) {
                                zipCode = lineString;
                            } else if (fieldType.equals(LocationImportTemplateFieldType.COUNTRY)) {
                                country = lineString;
                            }/* else if (fieldType.equals(LocationImportTemplateFieldType.LAT)) {
                                try {
                                    lat = new BigDecimal(lineString);
                                } catch (NumberFormatException e) {
                                    logger.error("Error encountered while parsing lat", e);
                                }
                            } else if (fieldType.equals(LocationImportTemplateFieldType.LNG)) {
                                try {
                                    lng = new BigDecimal(lineString);
                                } catch (NumberFormatException e) {
                                    logger.error("Error encountered while parsing lng", e);
                                }
                            }*/
                        }
                        j++;
                    }

                    if (type == null) {
                        throw new IllegalStateException("Location type is required.");
                    }

                    LocationModel location;
                    if (type.equals(LocationType.DC)) {
                        if (dcLocation != null) {
                            throw new IllegalStateException("There is more than one DC location.");
                        }

                        location = dcLocation = new DCModel(label, address, city, state, zipCode, country);
                    } else if (type.equals(LocationType.DELIVERY)) {
                        location = new DeliveryModel(label, address, city, state, zipCode, country);
                    } else if (type.equals(LocationType.PICKUP)) {
                        location = new PickupModel(label, address, city, state, zipCode, country);
                    } else if (type.equals(LocationType.RETURN)) {
                        location = new ReturnModel(label, address, city, state, zipCode, country);
                    } else {
                        throw new IllegalStateException(Utilities.buildString("Unsupported location type \"", type, "\"."));
                    }
                    //location.setId(String.valueOf(i + 1));
                    location.setStatus(LocationStatus.NG);
                    locations.add(location);

                    //i++;
                }

                if (!locations.isEmpty()) {
                    if (dcLocation == null) {
                        throw new IllegalStateException("There is no DC location.");
                    }

                    /*String dcFullAddress = null;
                    if (dcLocation.getAddress() != null) {
                        dcFullAddress = GeodataUtilities.buildLocationFullAddress(dcLocation);
                    }*/

                    //boolean dcGeocoded = false;
                    //boolean dcGeocodingCounted = false;
                    for (LocationModel location : locations) {
                        if (location instanceof DCModel) {
                            continue;
                        }
                        location.setDc(dcLocation);

                        /*if (location.getAddress() != null) {
                            String fullAddress = GeodataUtilities.buildLocationFullAddress(location);

                            logger.debug("Geocoding the address: {}...", fullAddress);

                            *//*GeocoderRequest geocoderRequest = new GeocoderRequestBuilder().setAddress(fullAddress).getGeocoderRequest();
                            GeocodeResponse geocoderResponse = geocoder.geocode(geocoderRequest);
                            List<GeocoderResult> results = geocoderResponse.getResults();
                            if (results != null && results.size() > 0) {
                                LatLng latLng = results.get(0).getGeometry().getLocation();
                                logger.debug("\tlat = {}, lng = {}", latLng.getLat(), latLng.getLng());

                                status = LocationStatus.G;*//*

                            logger.debug("Geocoding directions: from {}, to {}...", dcFullAddress, fullAddress);

                            int attempts = 0;
                            boolean geocodingFailed = true;
                            while (attempts < 3) {
                                DirectionsRequest directionsRequest = new DirectionsRequestBuilder().setOrigin(dcFullAddress).setDestination(fullAddress).getDirectionsRequest();
                                DirectionsResponse directionsResponse = directionsGeocoder.directions(directionsRequest);
                                logger.debug("Geocoding response: {}", directionsResponse);

                                if (directionsResponse != null && directionsResponse.getStatus() != null) {
                                    if (directionsResponse.getStatus().equals(DirectionsStatus.OK)) {
                                        List<com.google.code.geocoder.model.DirectionsRoute> routes = directionsResponse.getRoutes();

                                        com.google.code.geocoder.model.DirectionsRoute route = routes.get(0);
                                        List<com.google.code.geocoder.model.DirectionsLeg> legs = route.getLegs();

                                        if (!dcGeocoded) {
                                            com.google.code.geocoder.model.LatLng dcLatLng = legs.get(0).getStartLocation();
                                            logger.debug("\tdcLat = {}, dcLng = {}", dcLatLng.getLat(), dcLatLng.getLng());

                                            dcLocation.setLatLng(geocoderLatLngToLatLng.convert(dcLatLng));
                                            dcLocation.setStatus(LocationStatus.G);

                                            if (!dcGeocodingCounted) {
                                                geocodedLocationCount++;
                                                dcGeocodingCounted = true;
                                            }

                                            dcGeocoded = true;
                                        }

                                        com.google.code.geocoder.model.LatLng latLng = legs.get(legs.size() - 1).getEndLocation();
                                        logger.debug("\tlat = {}, lng = {}", latLng.getLat(), latLng.getLng());

                                        location.setLatLng(geocoderLatLngToLatLng.convert(latLng));
                                        location.setRoutes(geocoderDirectionsRouteToDirectionsRoute.convert(routes));
                                        location.setStatus(LocationStatus.G);

                                        // All web services. Rate limit of 10 requests per second, per web service.
                                        pauseGeocoding(geocodingDefaultPausingInterval, directionsResponse.getStatus());

                                        geocodingFailed = false;
                                        break;
                                    } else if (directionsResponse.getStatus().equals(DirectionsStatus.OVER_QUERY_LIMIT)) {
                                        // Upon receiving a response with status code OVER_QUERY_LIMIT, your application should determine which usage limit has been exceeded.
                                        // This can be done by pausing for 2 seconds and resending the same request. If status code is still OVER_QUERY_LIMIT, your application is sending too many requests per day.
                                        // Otherwise, your application is sending too many requests per second.
                                        pauseGeocoding(geocodingOverQueryLimitPausingInterval, directionsResponse.getStatus());
                                    } else {
                                        // All web services. Rate limit of 10 requests per second, per web service.
                                        pauseGeocoding(geocodingDefaultPausingInterval, directionsResponse.getStatus());

                                        break;
                                    }
                                }

                                attempts++;
                            }

                            if (geocodingFailed) {
                                logger.debug("geocoding failed");

                                if (!dcGeocoded) {
                                    dcLocation.setStatus(LocationStatus.GF);

                                    if (!dcGeocodingCounted) {
                                        geocodedLocationCount++;
                                        dcGeocodingCounted = true;
                                    }
                                }

                                location.setStatus(LocationStatus.GF);
                            }

                            geocodedLocationCount++;
                            geocodedDirectionsCount++;
                        //} else if (lat == null || lng == null) {
                        }*/
                    }

                    GeodataUtilities.updateLocationsPosition(locations);

                    GeodataStatistics statistics = new GeodataStatistics();
                    statistics.setNotGeocodedLocationCount(locations.size());
                    Date nowGMTDate = Utilities.getGMTDate();
                    GeodataModel geodata = new GeodataModel(GeodataStatus.NEW/*GeodataStatus.GEOCODED*//*, locations*/, user, sessionId, statistics, nowGMTDate);
                    geodataRepository.save(geodata);
                    //GeodataLocationsModel geodataLocations = new GeodataLocationsModel(locations, geodata.getId());
                    //geodataLocationsRepository.save(geodataLocations);
                    for (LocationModel location : locations) {
                        location.setGeodataId(geodata.getId());
                    }
                    locationRepository.save(locations);
                    logger.debug("importLocations: Geodata saved successfully");

                    userConfiguration.setImportedLocationCount(importedLocationCount);
                    //userConfiguration.setGeocodedLocationCount(geocodedLocationCount);
                    //userConfiguration.setGeocodedDirectionsCount(geocodedDirectionsCount);
                    userRepository.save(user);

                    //return buildGeodataResponse(geodata, geodataLocations, /*username*/sessionId);
                    return buildGeodataResponse(geodata, locations, /*username*/sessionId);
                }
            } else {
                throw new IllegalStateException("There is no Location Import Template defined");
            }
            return null;
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    /**
     * Exports the Geodata.
     *
     * @param id the Geodata id.
     *
     * @return the exported file bytes http entity.
     */
    public HttpEntity<byte[]> exportGeodata(String id, /*String username*/String sessionId) {
        try {
//            //LocationImportTemplateModel locationImportTemplate = locationImportTemplateRepository.findOne(locationImportTemplateId);
//            LocationImportTemplateDTModel locationImportTemplate = RuntimeConfigurationInstance.INSTANCE.getPrimaryLocationImportTemplate();
//            if (locationImportTemplate != null) {
//                //List<LocationImportTemplate.Field> fields = locationImportTemplate.getFields();
//                List<LocationImportTemplateDTModel.Field> fields = locationImportTemplate.getFields();

                GeodataModel geodata = geodataRepository.findOne(id);
                if (geodata != null) {
                    checkGeodataSessionId(geodata, sessionId);

                    StringBuilder stringBuilder = new StringBuilder();

                    //GeodataLocations geodataLocations = geodataLocationsRepository.findByGeodataId(id);
                    List<? extends LocationModel> locations = locationRepository.findByGeodataId(geodata.getId());
                    //for (LocationModel location : /*geodata*/geodataLocations.getLocations()) {
                    for (LocationModel location : locations) {
                        // location import template field count + 2 for lat and lng
                        String[] lineStrings = new String[9/*fields.size() + 2*/];

                        int j = 0;

                        String type;
                        if (location instanceof DCModel) {
                            type = LocationType.DC;
                        } else if (location instanceof DeliveryModel) {
                            type = LocationType.DELIVERY;
                        } else if (location instanceof PickupModel) {
                            type = LocationType.PICKUP;
                        } else if (location instanceof ReturnModel) {
                            type = LocationType.RETURN;
                        } else {
                            throw new IllegalStateException(Utilities.buildString("Unsupported location type \"", location.getClass().getName(), "\"."));
                        }
                        lineStrings[j++] = type;

                        //lineStrings[j++] = location.getDc();

                        lineStrings[j++] = StringUtils.defaultString(location.getLabel());

                        lineStrings[j++] = StringUtils.defaultString(location.getAddress());

                        lineStrings[j++] = StringUtils.defaultString(location.getCity());

                        lineStrings[j++] = StringUtils.defaultString(location.getState());

                        lineStrings[j++] = StringUtils.defaultString(location.getZipCode());

                        lineStrings[j++] = StringUtils.defaultString(location.getCountry());

                        LatLng latLng;
                        String latString;
                        String lngString;
                        if ((latLng = location.getLatLng()) != null) {
                            latString = latLng.getLat().toString();
                            lngString = latLng.getLng().toString();
                        } else {
                            latString = "";
                            lngString = "";
                        }
                        lineStrings[j++] = latString;
                        lineStrings[j++] = lngString;

                        Utilities.appendCommaSeparatedString(stringBuilder, lineStrings);
                        stringBuilder.append(Utilities.lineSeparator());
                    }

                    // the exported Geodata file content
                    byte[] content = stringBuilder.toString().getBytes();
                    // the exported Geodata file content type
                    String contentType = LocationImportSupportedMimeType.TEXT_CSV;
                    // the exported Geodata filename
                    String filename = GeodataUtilities.buildGeodataFilename(geodata);

                    logger.debug("exportGeodata: returning the byte array http entity, contentType = {}, contentLength = {}, filename = {}", contentType, content.length, filename);

                    return Utilities.buildByteArrayHttpEntity(contentType, content, filename);
                }
//            }
        } catch (Exception e) {
            throw new ServiceException(e);
        }

        return null;
    }

    /**
     * Checks the Geodata session id.
     *
     * @param geodata the Geodata.
     * @param sessionId the session id.
     *
     * @throws IllegalArgumentException if <code>geodata</code> is not equal to
     * <code>sessionId</code>.
     */
    private void checkGeodataSessionId(GeodataModel geodata, String sessionId) {
        if (geodata.getSessionId() == null || !geodata.getSessionId().equals(sessionId)) {
            logger.debug("checkGeodataSessionId: Illegal access to geodata = {} using the sessionId = {} attempt", geodata.getId(), sessionId);
            throw new IllegalArgumentException("Illegal access attempt");
        }
    }

    /**
     * Pauses geocoding.
     *
     * @param millis the pausing interval in milliseconds.
     * @param status the pausing status description.
     */
    private void pauseGeocoding(long millis, String status) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            StringBuilder messageBuilder = new StringBuilder();
            messageBuilder.append("Exception was thrown when pause geocoding for ").append(millis).append(" milliseconds");
            if (status != null) {
                messageBuilder.append(" (").append(status).append(")");
            }
            logger.debug(messageBuilder.toString(), e);
        }
    }
    /**
     * Pauses geocoding.
     *
     * @param millis the pausing interval in milliseconds.
     * @param status the directions geocoding status.
     */
    private void pauseGeocoding(long millis, DirectionsStatus status) {
        pauseGeocoding(millis, status.name());
    }

    /**
     * Checks the imported Locations count.
     *
     * @param importedLocationCount the imported Locations count.
     * @param initialImportedLocationCount the initial imported Locations count.
     *
     * @throws IllegalStateException if <code>importedLocationCount</code> is more than
     * <code>initialImportedLocationCount</code>.
     */
    private void checkImportedLocationCount(int importedLocationCount, int initialImportedLocationCount) {
        if (importedLocationCount > maxImportedLocationCount) {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("You can not exceed the maximum imported locations count, ");
            if (maxImportedLocationCount - initialImportedLocationCount > 0) {
                stringBuilder.append(" available: ").append(maxImportedLocationCount - initialImportedLocationCount);
            } else {
                stringBuilder.append("available count was exceeded");
            }
            stringBuilder.append(".");
            throw new IllegalStateException(stringBuilder.toString());
        }
    }

    /**
     * Builds the Geodata wrapped response.
     *
     * @param geodata The geodata.
     * @param locations The Locations.
     //* @param username The username.
     * @param sessionId The session id.
     *
     * @return The Geodata wrapped response.
     */
    //private ResponseWrapper<GeodataResponseDTModel> buildGeodataResponse(GeodataModel geodata, GeodataLocationsModel geodataLocations, /*String username*/String sessionId) {
    private ResponseWrapper<GeodataResponse> buildGeodataResponse(GeodataModel geodata, List<? extends LocationModel> locations, /*String username*/String sessionId) {
        GeodataResponseDTModelBuilder builder = new GeodataResponseDTModelBuilder(conversionUtilities);
        builder.setGeodata(geodata);
        //builder.setGeodataLocations(geodataLocations);
        builder.setLocations(locations);
        List<GeodataModel> geodataList = geodataRepository.findBySessionId(sessionId, GeodataModelSorting.BY_CREATION_DATE_DESC);
        //List<GeodataModel> geodataList = geodataRepository.findByUserUsername(username, GeodataModelSorting.BY_CREATION_DATE_DESC);
        if (geodataList != null) {
            builder.setGeodataList(geodataList);
        }
        return builder.buildWrapper();
    }
}
